//***********************************************************************
//	File:		Play.cpp
//	Author:		
//	Course:		SGP 1405
//	Purpose:	
//***********************************************************************
//***********************************************************************
//	Purposfully left blank for all comments regarding this file 
//	
//	
//	
//***********************************************************************

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#include "Play.h"
#include "../source/Game.h"
#include "../GameStates/MainMenu.h"

#include "../SGD Wrappers/SGD_AudioManager.h"
#include "../SGD Wrappers/SGD_GraphicsManager.h"
#include "../SGD Wrappers/SGD_InputManager.h"

#include "../SGD Wrappers/SGD_MessageManager.h"
#include "../SGD Wrappers/SGD_Message.h"
#include "../Message/MessageID.h"
#include "../Message/CreateEntityMessage.h"
#include "../Message/CreateProjectileMessage.h"
#include "../Message/DestroyEntityMessage.h"
#include "../Message/UseAbilityMessage.h"

#include "../SGD Wrappers/SGD_EventManager.h"
#include "../SGD Wrappers/SGD_Event.h"

#include "../GameObjects/WoodenCrate.h"

#include "../GameObjects/Entity.h"
#include "../GameObjects/Player.h"
#include "../GameObjects/Anklebiter.h"
#include "../GameObjects/Basher.h"

#include "../GameObjects/Bullet.h"
#include "../GameObjects/SniperBullet.h"
#include "../GameObjects/Flame.h"
#include "../GameObjects/HeatSeeker.h"

#include "../GameObjects/ConcussionBlast.h"
#include "../GameObjects/CryoNet.h"
#include "../GameObjects/ConfusionGas.h"

#include "../GameObjects/EntityManager.h"

#include "../TileSystem/TileSystem.h"
#include "../ParticleSystem/ParticleSystem.h"


Play* Play::GetInstance ( void )
{
	static Play s_pInstance;
	return &s_pInstance;
}

void	Play::Enter ( void )
{
	SGD::InputManager* pInput = SGD::InputManager::GetInstance ();

	m_ptWorldSize.x = Game::GetInstance ()->GetScreenWidth ();
	m_ptWorldSize.y = Game::GetInstance ()->GetScreenHeight ();

	TILESYSTEM->LoadLevel ( "resource/data/Level1.xml" );
	//PARTICLESYSTEM->LoadEmitter("resource/data/testParticle1.xml");

	m_ptCameraPos.x = 0;
	m_ptCameraPos.y = 0;


	Game* pAssets = Game::GetInstance ();

	// Initialize Event Manager & Message Manager 
	m_pEvents = SGD::EventManager::GetInstance ();
	m_pEvents->Initialize ();

	m_pMessages = SGD::MessageManager::GetInstance ();
	m_pMessages->Initialize ( &MessageProc );


	//Set Up Entity Manager; 
	m_pEntities = new EntityManager ();

	CreateEntityMessage* playerOne = new CreateEntityMessage ( Entity::EntityType::ENT_PLAYER , TILESYSTEM->GetPlayerSpawn () );
	playerOne->QueueMessage ();
	playerOne = nullptr;

	CreateEntityMessage* playerToo = new CreateEntityMessage ( Entity::EntityType::ENT_PLAYERTOO , TILESYSTEM->GetPlayerSpawn () );
	playerToo->QueueMessage ();
	playerToo = nullptr;

	// Instantiate the listener to catch the INCREASE_SCORE event
	m_pListener = new PlayListener ();
}

void	Play::Exit ( void )
{
	if ( m_pListener != nullptr )
	{
		delete m_pListener;
		m_pListener = nullptr;
	}


	TiXmlDocument doc;

	TiXmlDeclaration* pDecl = new TiXmlDeclaration ( "1.0" , "utf-8" , "" );
	doc.LinkEndChild ( pDecl );

	TiXmlElement* pRoot = new TiXmlElement ( "SavedGameStatus" );
	doc.LinkEndChild ( pRoot );

	TiXmlElement* pCurrentLevel = new TiXmlElement ( "CurrentLevel" );
	pCurrentLevel->SetAttribute ( "CurrLevel" , Game::GetInstance ()->GetWhatLevel () );
	pRoot->LinkEndChild ( pCurrentLevel );

	TiXmlElement* pPlayerLives = new TiXmlElement ( "CurrentLives" );
	pPlayerLives->SetAttribute ( "P1CurrLives" , m_pPlayer1->GetLives () );
	pPlayerLives->SetAttribute ( "P2CurrLives" , m_pPlayer2->GetLives () );
	pRoot->LinkEndChild ( pPlayerLives );

	TiXmlElement* pPlayerScore = new TiXmlElement ( "CurrentScore" );
	pPlayerScore->SetAttribute ( "P1CurrScore" , m_pPlayer1->GetScore () );
	pPlayerScore->SetAttribute ( "P2CurrScore" , m_pPlayer2->GetScore () );
	pRoot->LinkEndChild ( pPlayerScore );

	if ( Game::GetInstance ()->GetSavesCursor () == 0 )
	{
		doc.SaveFile ( "resource/data/SaveSlot1.xml" );
	}
	else if ( Game::GetInstance ()->GetSavesCursor () == 1 )
	{
		doc.SaveFile ( "resource/data/SaveSlot2.xml" );
	}
	else if ( Game::GetInstance ()->GetSavesCursor () == 2 )
	{
		doc.SaveFile ( "resource/data/SaveSlot3.xml" );
	}

	TILESYSTEM->UnloadLevel ();
	PARTICLESYSTEM->Exit();

	// Getting rid of entities
	m_pEntities->RemoveAll ();
	delete m_pEntities;
	m_pEntities = nullptr;

	delete m_pPlayer1;
	m_pPlayer1 = nullptr;

	delete m_pPlayer2;
	m_pPlayer2 = nullptr;

	//Getting Rid of Events 
	m_pEvents->Terminate ();
	m_pEvents = nullptr;
	SGD::EventManager::DeleteInstance ();

	//Getting rid of Messages
	m_pMessages->Terminate ();
	m_pMessages = nullptr;
	SGD::MessageManager::DeleteInstance ();
}

bool	Play::Input ( void )
{
	SGD::InputManager * pInput = SGD::InputManager::GetInstance ();

	if ( pInput->IsKeyPressed ( SGD::Key::Escape ) == true )
	{
		Game::GetInstance ()->ChangeState ( MainMenu::GetInstance () );
	}

	/*if ( pInput->IsKeyDown ( SGD::Key::Up ) == true )
		m_ptCameraPos.y -= 10;
	if ( pInput->IsKeyDown ( SGD::Key::Down ) == true )
		m_ptCameraPos.y += 10;
	if ( pInput->IsKeyDown ( SGD::Key::Left ) == true )
		m_ptCameraPos.x -= 10;
	if ( pInput->IsKeyDown ( SGD::Key::Right ) == true )
		m_ptCameraPos.x += 10;*/



	if ( pInput->IsButtonPressed ( 0 , 7 ) )
		m_bGamePaused = !m_bGamePaused;

	/*SGD::Vector dir = pInput->GetLeftJoystick(0);

	if (dir.x < 0)
	m_ptCameraPos.x -= 10;
	if (dir.x > 0)
	m_ptCameraPos.x += 10;
	if (dir.y < 0)
	m_ptCameraPos.y -= 10;
	if (dir.y > 0)
	m_ptCameraPos.y += 10; */

	return true;
}

void	Play::Update ( float dt )
{
	if ( m_bGamePaused == false )
	{

		m_pEntities->UpdateAll ( dt );
		//PARTICLESYSTEM->Update(dt);
	
		// Process events and messages
		m_pEvents->Update ();
		m_pMessages->Update ();
		if (GetPlayer() != nullptr)
		{
			m_ptCameraPos.x = m_pPlayer1->GetPosition().x - Game::GetInstance()->GetScreenWidth() / 2;
			m_ptCameraPos.y = m_pPlayer1->GetPosition().y - Game::GetInstance()->GetScreenHeight() / 2;
		}
		
	}

}

void	Play::Render ( void )
{
	int width = (int)Game::GetInstance ()->GetScreenWidth ();
	int height = (int)Game::GetInstance ()->GetScreenHeight ();

	// Render the background
	TILESYSTEM->Render ();
	//PARTICLESYSTEM->Render();
	m_pEntities->RenderAll ();
	

	BitmapFont* pFont = Game::GetInstance ()->GetFont ();
	std::stringstream x;
	x << m_ptCameraPos.x;
	std::stringstream y;
	y << m_ptCameraPos.y;

	pFont->Draw ( x.str ().c_str () , 50 , 50 , 1 , { 255 , 255 , 200 , 0 } );
	pFont->Draw ( y.str ().c_str () , 50 , 100 , 1 , { 255 , 255 , 200 , 0 } );

	if ( m_bGamePaused == true )
	{
		pFont->Draw ( "Game Paused" , width / 2 - 75 , height / 2 - 32 , 1 , { 255 , 200 , 0 } );
	}
}


/**************************************************************/
// MessageProc
//	- process messages queued in the MessageManager
//	- STATIC METHOD
//		- does NOT have invoking object!!!
//		- must use singleton to access members
/*static*/ void Play::MessageProc ( const SGD::Message* pMsg )
{
#pragma warning( push )
#pragma warning( 1 : 4061 )

	//Message Type
	switch ( pMsg->GetMessageID () )
	{
	case MessageID::CREATE_ENTITY_MESSAGE:
	{
		const CreateEntityMessage* pCreateEntity =
			dynamic_cast<const CreateEntityMessage*>(pMsg);

		unsigned int type = pCreateEntity->GetType ();
		float y = (float)pCreateEntity->GetSpawnPoint ().y;
		float x = (float)pCreateEntity->GetSpawnPoint ().x;
		switch ( type )
		{
		case Entity::EntityType::ENT_ANKLEBITER:
		{
			Anklebiter* temp = new Anklebiter ( x , y );
			temp->SetImage ( Game::GetInstance ()->GetImage ( Game::ANKLEBITER ) );
			Play::GetInstance ()->m_pEntities->AddEntity ( temp , ENEMIES );
			temp->Release ();
		}
			break;
		case Entity::EntityType::ENT_BASHER:
		{
			//do all the stuff to make a basher
		  Basher* Bashertemp = new Basher(x, y);

		  Bashertemp->Release();


		}
			break;
		case Entity::EntityType::ENT_MECHATROOPER:
		{
			//do all the stuff to make a MechaTrooper
		}
			break;
		case Entity::EntityType::ENT_MOTHERBRAIN:
		{
			//do all the stuff to make a MotherBrain
		}
			break;
		case Entity::EntityType::ENT_PWNER:
		{
			//do all the stuff to make a Pwner
		}
			break;
		case Entity::EntityType::ENT_RAIDER:
		{
			//do all the stuff to make a Raider
		}
			break;
		case Entity::EntityType::ENT_SPIDERMINE:
		{
			//do all the stuff to make a Spider Mine
		}
			break;
		case Entity::EntityType::ENT_STRIKER:
		{
			//do all the stuff to make a Striker
		}
			break;
		case Entity::EntityType::ENT_TANKOSAURUS:
		{
			//do all the stuff to make a TankoSaurus
		}
		case Entity::EntityType::ENT_CONCRETEBARRIER:
		{
			//call ConcreteBarrier's ctor
		}
			break;
		case Entity::EntityType::ENT_WOODENCRATE:
		{
			WoodenCrate* crate = new WoodenCrate(x, y);
			Play::GetInstance()->m_pEntities->AddEntity(crate, OBJECTS);
			crate->Release();
		}
			break;
		case Entity::EntityType::ENT_ELECTRIC_FENCE:
		{
			//call Electric Fences ctor
		}
			break;
		case Entity::EntityType::ENT_FIRETRAP:
		{
			//call FireTrap's ctor
		}
			break;
		case Entity::EntityType::ENT_HEALTHPACK:
		{
			//call Healthpack's ctor
		}
			break;
		case Entity::EntityType::ENT_PLAYER:
		{
			Play::GetInstance ()->m_pPlayer1 = new Player ( x , y , false );
			Play::GetInstance ()->m_pEntities->AddEntity ( Play::GetInstance ()->m_pPlayer1 , PLAYERS );
		}
			break;
		case Entity::EntityType::ENT_PLAYERTOO:
		{
			Play::GetInstance ()->m_pPlayer2 = new Player ( x , y , true );
			Play::GetInstance ()->m_pEntities->AddEntity ( Play::GetInstance ()->m_pPlayer2 , PLAYERS );
		}
			break;
		default:
		{
			OutputDebugStringW ( L"Game::MessageProc - unknown Enemy type created \n" );
		}
		}

	}
		break;
	case MessageID::CREATE_PROJECTILE_MESSAGE:
	{
		const CreateProjectileMessage* pCreateProjectile =
			dynamic_cast<const CreateProjectileMessage*>(pMsg);

		Entity* projectileSender = pCreateProjectile->GetSender ();

		switch ( pCreateProjectile->GetType () )
		{
		case Entity::EntityType::ENT_BULLET:
		{
			//call bullets ctor
			Bullet* pBullet = new Bullet ( projectileSender->GetPosition ().x , projectileSender->GetPosition ().y , projectileSender );
			Play::GetInstance ()->m_pEntities->AddEntity ( pBullet , PROJECTILES );
			pBullet->Release ();
		}
			break;
		case Entity::EntityType::ENT_HEATSEEKER:
		{
			//call HeatSeeker's ctor
			HeatSeeker* pHeatSeeker = new HeatSeeker(projectileSender->GetPosition().x, projectileSender->GetPosition().y, projectileSender);
			Play::GetInstance()->m_pEntities->AddEntity(pHeatSeeker, PROJECTILES);
			pHeatSeeker->Release();
		}
			break;
		case Entity::EntityType::ENT_FLAME:
		{
			Flame* pFlame = new Flame(projectileSender->GetPosition().x, projectileSender->GetPosition().y, projectileSender);
			Play::GetInstance()->m_pEntities->AddEntity(pFlame, PROJECTILES); 
			pFlame->Release(); 
		}
			break;
		case Entity::EntityType::ENT_TESLA:
		{
			//call Tesla's ctor
		}
			break;
		case Entity::EntityType::ENT_SNIPER_BULLET:
		{
			SniperBullet* pBullet = new SniperBullet ( projectileSender->GetPosition ().x , projectileSender->GetPosition ().y , projectileSender );
			Play::GetInstance ()->m_pEntities->AddEntity ( pBullet , PROJECTILES );
			pBullet->Release ();
		}
		default:
			OutputDebugStringW ( L"Game::MessageProc - unknown Bullet Created\n" );
		}

	}
		break;
	case MessageID::USE_ABILITY_MESSAGE:
	{
		const UseAbilityMessage* pUseAbilityMsg =
			dynamic_cast<const UseAbilityMessage*>(pMsg);

		Entity* entityType = pUseAbilityMsg->GetSender ();

		switch ( pUseAbilityMsg->GetAbilityType() )
		{
		case Entity::EntityType::ENT_CONCUSSIONBLAST:
		{
			ConcussionBlast* pConcussionBlast = new ConcussionBlast ( entityType->GetPosition ().x , entityType->GetPosition ().y , entityType );
			Play::GetInstance ()->m_pEntities->AddEntity ( pConcussionBlast , ABILITIES );
			pConcussionBlast->Release ();
		}
			break;
		case Entity::EntityType::ENT_CONFUSION_GAS:
		{
			//call Confusion Gas' ctor
			ConfusionGas* pConfusionGas = new ConfusionGas ( entityType->GetPosition ().x , entityType->GetPosition ().y , entityType );
			Play::GetInstance ()->m_pEntities->AddEntity ( pConfusionGas , ABILITIES );
			pConfusionGas->Release ();
		}
			break;
		case Entity::EntityType::ENT_CRYONET:
		{
			//call CryoNet's ctor
			CryoNet* pCryoNet = new CryoNet ( entityType->GetPosition ().x , entityType->GetPosition ().y , entityType );
			Play::GetInstance ()->m_pEntities->AddEntity ( pCryoNet , ABILITIES );
			pCryoNet->Release ();
		}
			break;
		}
		break;
	}
	case MessageID::DESTROY_ENTITY_MESSAGE:
	{
		const DestroyEntityMessage* pDestroyMsg =
			dynamic_cast<const DestroyEntityMessage*>(pMsg);

		Entity* entityType = pDestroyMsg->GetSender ();

		switch ( entityType->GetType () )
		{
		case Entity::EntityType::ENT_ANKLEBITER:
		{
			//Destroy Anklebiter here
		}
			break;
		case Entity::EntityType::ENT_BASHER:
		{
			//Destroy Basher here
		}
			break;
		case Entity::EntityType::ENT_BULLET:
		{
			Play::GetInstance ()->m_pEntities->RemoveEntity ( entityType );
		}
			break;
		case Entity::EntityType::ENT_SNIPER_BULLET:
		{
			Play::GetInstance ()->m_pEntities->RemoveEntity ( entityType );
		}
			break;
		case Entity::EntityType::ENT_CONCRETEBARRIER:
		{
			//Destroy ConcreteBarrier here
		}
			break;
		case Entity::EntityType::ENT_CONCUSSIONBLAST:
		{
			// Destroy ConcussionBlast here
			Play::GetInstance ()->m_pEntities->RemoveEntity ( entityType );
		}
			break;
		case Entity::EntityType::ENT_CONFUSION_GAS:
		{
			// Destroy ConfusionGas here
			Play::GetInstance ()->m_pEntities->RemoveEntity ( entityType );
		}
			break;
		case Entity::EntityType::ENT_CRYONET:
		{
			// Destroy CryoNet here
			Play::GetInstance ()->m_pEntities->RemoveEntity ( entityType );
		}
			break;
		case Entity::EntityType::ENT_DISRUPTOR:
		{
			// Destroy Disruptor here
		}
			break;
		case Entity::EntityType::ENT_ELECTRIC_FENCE:
		{
			// Destroy Electric Fence here
		}
			break;
		case Entity::EntityType::ENT_ENGINEER:
		{
			//Destroy Engineer here
		}
			break;
		case Entity::EntityType::ENT_EXPLOSIVEBARREL:
		{
			// Destroy Explosive Barrel here
		}
			break;
		case Entity::EntityType::ENT_FIRETRAP:
		{
			// Destroy FireTrap here
		}
			break;
		case Entity::EntityType::ENT_FLAME:
		{
			Play::GetInstance()->m_pEntities->RemoveEntity(entityType);
		}
			break;
		case Entity::EntityType::ENT_GRENADE:
		{
			// Destroy Grenade here
		}
			break;
		case Entity::EntityType::ENT_HEALTHPACK:
		{
			// Destroy Healthpack here
		}
			break;
		case Entity::EntityType::ENT_HEATSEEKER:
		{
			Play::GetInstance()->m_pEntities->RemoveEntity(entityType);
		}
			break; 
		case Entity::EntityType::ENT_MECHATROOPER:
		{
			// Destroy MechaTrooper here
		}
			break;
		case Entity::EntityType::ENT_METALCRATE:
		{
			// Destroy MetalCrate here
		}
			break;
		case Entity::EntityType::ENT_MOTHERBRAIN:
		{
			// Destroy MotherBrain here
		}
			break;
		case Entity::EntityType::ENT_NURSE:
		{
			// Destroy Nurse here
		}
			break;
		case Entity::EntityType::ENT_PLAYER:
		{
			// Destroy Player here
		}
			break;
		case Entity::EntityType::ENT_PWNER:
		{
			// Destroy Pwner here
		}
			break;
		case Entity::EntityType::ENT_RAIDER:
		{
			//Destroy Raider here
		}
			break;
		case Entity::EntityType::ENT_SPIDERMINE:
		{
			// Destroy Spidermine here
		}
			break;
		case Entity::EntityType::ENT_STRIKER:
		{
			// Destroy Striker here
		}
			break;
		case Entity::EntityType::ENT_TANKOSAURUS:
		{
			//Destroy Tankosaurus here
		}
			break;
		case Entity::EntityType::ENT_TESLA:
		{
			//Destroy tesla here
		}
			break;
		case Entity::EntityType::ENT_WOODENCRATE:
		{
			// Destroy Wooden Crate here
		}
			break;
		default:
			OutputDebugStringW ( L"Game::MessageProc - unknown entity to be destroyed\n" );
			break;

		}


	}
		break;
	default:
		OutputDebugStringW ( L"Game::MessageProc - unknown message id\n" );
		break;
	}


}

// GetCameraRect
//	Returns the realestate being shown to the user
SGD::Rectangle	Play::GetCameraRect ( void ) const
{
	SGD::Rectangle viewPort;

	viewPort.left = m_ptCameraPos.x;
	viewPort.top = m_ptCameraPos.y;
	viewPort.right = viewPort.left + m_ptWorldSize.x;
	viewPort.bottom = viewPort.top + m_ptWorldSize.y;

	return viewPort;
}

// GetCameraPos
//	Returns the cameras position. This will always be
//	roughly half the screen width to the left of the player
//	and roughly half the screen height above the player plus
//	or minus the dead zone allowed
SGD::Point		Play::GetCameraPos ( void ) const
{
	return m_ptCameraPos;
}

// GetScreenSize
//	Set once the play state is entered so the Play stat does not
//	have to call Game's GetInstance() function constantly
SGD::Point		Play::GetScreenSize ( void ) const
{
	return m_ptWorldSize;
}

Player* Play::GetPlayer ( void )
{
	if ( m_pPlayer1 != nullptr )
		return m_pPlayer1;
	else
		return m_pPlayer2;
}